#include "ly/layout.hpp"
#include "ly/manager.hpp"

#include <entt/entt.hpp>

namespace ly {

struct LayoutOp : public Op {
  LayoutOp() {}
  virtual ~LayoutOp() {}

  virtual void redo(Layout*) const = 0;
  virtual void undo(Layout*) const = 0;
};

struct SetLayoutPropId : public LayoutOp {
  SetLayoutPropId(properties_id_type f, properties_id_type t) : m_from(f), m_to(t) {}

  virtual void redo(Layout* layout) const { layout->prop_id(m_to); }

  virtual void undo(Layout* layout) const { layout->prop_id(m_from); }

 private:
  properties_id_type m_from, m_to;
};

// struct SetLayoutTechName : public LayoutOp {
//   SetLayoutTechName(const std::string& from, const std::string& to) : m_from(from), m_to(to) {}

//   virtual void redo(Layout* layout) const { layout->set_technology_name_without_update(m_to); }

//   virtual void undo(Layout* layout) const { layout->set_technology_name_without_update(m_from); }

//  private:
//   std::string m_from, m_to;
// };

struct SetLayoutDBU : public LayoutOp {
  SetLayoutDBU(double f, double t) : m_from(f), m_to(t) {}

  virtual void redo(Layout* layout) const { layout->dbu(m_to); }

  virtual void undo(Layout* layout) const { layout->dbu(m_from); }

 private:
  double m_from, m_to;
};

struct RenameCellOp : public LayoutOp {
  RenameCellOp(cell_id_type i, const std::string& f, const std::string& t) : m_cell_id(i), m_from(f), m_to(t) {}

  virtual void redo(Layout* layout) const { layout->rename_cell(m_cell_id, m_to.c_str()); }

  virtual void undo(Layout* layout) const { layout->rename_cell(m_cell_id, m_from.c_str()); }

 private:
  cell_id_type m_cell_id;
  std::string m_from, m_to;
};

cell_id_type Layout::add_cell(const std::string& name) {
  auto ci = m_name_by_id.find(name);
  if (ci == m_name_by_id.end()) {
    auto new_id = m_cells.size();
    auto cell = Cell::create(name, this_sptr());
    cell->id(new_id);
    m_cells.push_back(cell);
    m_name_by_id[cell->name()] = new_id;
    return new_id;
  } else {
    return ci->second;
  }
}

void Layout::rename_cell(cell_id_type id, const char* name) {
  assert(id < m_cell_names.size());

  // if (strcmp(m_cell_names[id], name) != 0) {

  //   if (manager() && manager()->transacting()) {
  //     manager()->queue(this, new RenameCellOp(id, m_cell_names[id], name));
  //   }

  //   m_cell_map.erase(m_cell_names[id]);

  //   char* cp = new char[strlen(name) + 1];
  //   strcpy(cp, name);

  //   delete[] m_cell_names[id];
  //   m_cell_names[id] = cp;

  //   m_cell_map.insert(std::make_pair(cp, id));

  //   //  to enforce a redraw and a rebuild
  //   cell_name_changed();
  // }
}

void Layout::prop_id(properties_id_type id) {
  if (m_prop_id != id) {
    if (manager() && manager()->transacting()) {
      // manager()->queue(this, new SetLayoutPropId(m_prop_id, id));
    }
    m_prop_id = id;
  }
}

// const PCellHeader::sptr Layout::pcell_header(pcell_id_type pcell_id) const {
//   return (const_cast<ly::Layout*>(this))->pcell_header(pcell_id);
// }

PCellHeader::sptr Layout::pcell_header(pcell_id_type pcell_id) {
  if (pcell_id >= m_pcells.size()) {
    return 0;
  } else {
    return m_pcells[pcell_id];
  }
}

}  // namespace ly
